<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>
<meta name="description" content="A diagram with Graduated panels at the edges acting as rulers"/> 
<link rel="stylesheet" href="../assets/css/style.css"/> 
<!-- Copyright 1998-2021 by Northwoods Software Corporation. -->
<title>Diagram with Rulers</title>
</head>

<body>
  <!-- This top nav is not part of the sample code -->
  <nav id="navTop" class="w-full z-30 top-0 text-white bg-nwoods-primary">
    <div class="w-full container max-w-screen-lg mx-auto flex flex-wrap sm:flex-nowrap items-center justify-between mt-0 py-2">
      <div class="md:pl-4">
        <a class="text-white hover:text-white no-underline hover:no-underline
        font-bold text-2xl lg:text-4xl rounded-lg hover:bg-nwoods-secondary " href="../">
          <h1 class="mb-0 p-1 ">GoJS</h1>
        </a>
      </div>
      <button id="topnavButton" class="rounded-lg sm:hidden focus:outline-none focus:ring" aria-label="Navigation">
        <svg fill="currentColor" viewBox="0 0 20 20" class="w-6 h-6">
          <path id="topnavOpen" fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM9 15a1 1 0 011-1h6a1 1 0 110 2h-6a1 1 0 01-1-1z" clip-rule="evenodd"></path>
          <path id="topnavClosed" class="hidden" fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
        </svg>
      </button>
      <div id="topnavList" class="hidden lg:text-base sm:block items-center w-auto mt-0 text-white p-0 z-20">
        <ul class="list-reset list-none font-semibold flex justify-end flex-wrap sm:flex-nowrap items-center px-0 pb-0">
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../learn/">Learn</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../samples/">Samples</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../intro/">Intro</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../api/">API</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/products/register.html">Register</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="../download.html">Download</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://forum.nwoods.com/c/gojs/11">Forum</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/contact.html"
           target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/contact.html', 'contact');">Contact</a></li>
          <li class="p-1 sm:p-0"><a class="topnav-link" href="https://www.nwoods.com/sales/index.html"
           target="_blank" rel="noopener" onclick="getOutboundLink('https://www.nwoods.com/sales/index.html', 'buy');">Buy</a></li>
        </ul>
      </div>
    </div>
    <hr class="border-b border-gray-600 opacity-50 my-0 py-0" />
  </nav>
  <div class="md:flex flex-col md:flex-row md:min-h-screen w-full max-w-screen-xl mx-auto">
    <div id="navSide" class="flex flex-col w-full md:w-48 text-gray-700 bg-white flex-shrink-0"></div>
    <!-- * * * * * * * * * * * * * -->
    <!-- Start of GoJS sample code -->
    
    <script src="../release/go.js"></script>
    <div class="p-4 w-full">
    <script id="code">
    function init() {

      var $ = go.GraphObject.make;  // for conciseness in defining templates

      myDiagram =
        $(go.Diagram, "myDiagramDiv",  // create a Diagram for the DIV HTML element
          {
            "undoManager.isEnabled": true,  // enable undo & redo
            scrollMode: go.Diagram.InfiniteScroll,  // allow the diagram to be scrolled beyond content
            padding: 0,  // scales should be allowed right up against the edges of the viewport
            "grid.visible": true  // show that the ticks of the rulers always line up with the grid lines
          });

      myDiagram.nodeTemplate =
        $(go.Node, "Auto",
          $(go.Shape, "RoundedRectangle",
            { strokeWidth: 0, portId: "", fromLinkable: true, toLinkable: true },
            new go.Binding("fill", "color")),
          $(go.TextBlock,
            { margin: 8 },
            new go.Binding("text", "key"))
        );

      myDiagram.model = new go.GraphLinksModel(
        [
          { key: "Alpha", color: "lightblue" },
          { key: "Beta", color: "orange" },
          { key: "Gamma", color: "lightgreen" },
          { key: "Delta", color: "pink" }
        ],
        [
          { from: "Alpha", to: "Beta" },
          { from: "Alpha", to: "Gamma" },
          { from: "Beta", to: "Beta" },
          { from: "Gamma", to: "Delta" },
          { from: "Delta", to: "Alpha" }
        ]);

      // Keep references to the scales and indicators to easily update them
      var gradScaleHoriz =
        $(go.Part, "Graduated",
          { graduatedTickUnit: 10, pickable: false, layerName: "Grid", isAnimated: false },
          $(go.Shape, { geometryString: "M0 0 H400" }),
          $(go.Shape, { geometryString: "M0 0 V3", interval: 1 }),
          $(go.Shape, { geometryString: "M0 0 V15", interval: 5 }),
          $(go.TextBlock,
            {
              font: "10px sans-serif",
              interval: 5,
              alignmentFocus: go.Spot.TopLeft,
              segmentOffset: new go.Point(0, 7)
            }
          )
        );

      var gradScaleVert =
        $(go.Part, "Graduated",
          { graduatedTickUnit: 10, pickable: false, layerName: "Grid", isAnimated: false },
          $(go.Shape, { geometryString: "M0 0 V400" }),
          $(go.Shape, { geometryString: "M0 0 V3", interval: 1, alignmentFocus: go.Spot.Bottom }),
          $(go.Shape, { geometryString: "M0 0 V15", interval: 5, alignmentFocus: go.Spot.Bottom }),
          $(go.TextBlock,
            {
              font: "10px sans-serif",
              segmentOrientation: go.Link.OrientOpposite,
              interval: 5,
              alignmentFocus: go.Spot.BottomLeft,
              segmentOffset: new go.Point(0, -7)
            }
          )
        );

      var gradIndicatorHoriz =
        $(go.Part,
          {
            pickable: false, layerName: "Grid", visible: false,
            isAnimated: false, locationSpot: go.Spot.Top
          },
          $(go.Shape, { geometryString: "M0 0 V15", strokeWidth: 2, stroke: "red" })
        );

      var gradIndicatorVert =
        $(go.Part,
          {
            pickable: false, layerName: "Grid", visible: false,
            isAnimated: false, locationSpot: go.Spot.Left
          },
          $(go.Shape, { geometryString: "M0 0 H15", strokeWidth: 2, stroke: "red" })
        );

      // Add listeners to keep the scales/indicators in sync with the viewport
      myDiagram.addDiagramListener("InitialLayoutCompleted", setupScalesAndIndicators);
      myDiagram.mouseEnter = function() { showIndicators(true); };
      myDiagram.mouseLeave = function() { showIndicators(false); };
      myDiagram.addDiagramListener("ViewportBoundsChanged", function(e) { updateScales(); updateIndicators(); });

      // Override mousemove Tools such that doMouseMove will keep indicators in sync
      myDiagram.toolManager.doMouseMove = function() {
        go.ToolManager.prototype.doMouseMove.call(this);
        updateIndicators();
      }
      myDiagram.toolManager.linkingTool.doMouseMove = function() {
        go.LinkingTool.prototype.doMouseMove.call(this);
        updateIndicators();
      }
      myDiagram.toolManager.draggingTool.doMouseMove = function() {
        go.DraggingTool.prototype.doMouseMove.call(this);
        updateIndicators();
      }
      myDiagram.toolManager.dragSelectingTool.doMouseMove = function() {
        go.DragSelectingTool.prototype.doMouseMove.call(this);
        updateIndicators();
      }
      // No need to override PanningTool since the ViewportBoundsChanged listener will fire

      function setupScalesAndIndicators() {
        myDiagram.commit(function(d) {
          // Add each node to the diagram
          d.add(gradScaleHoriz);
          d.add(gradScaleVert);
          d.add(gradIndicatorHoriz);
          d.add(gradIndicatorVert);
          updateScales();
          updateIndicators();
        }, null);  // null says to skip UndoManager recording of changes
      }

      function updateScales(vb) {
        if (!vb) vb = myDiagram.viewportBounds;
        if (!vb.isReal()) return;
        myDiagram.commit(function(diag) {
          // Update properties of horizontal scale to reflect viewport
          gradScaleHoriz.elt(0).width = vb.width * diag.scale;
          gradScaleHoriz.location = new go.Point(vb.x, vb.y);
          gradScaleHoriz.graduatedMin = vb.x;
          gradScaleHoriz.graduatedMax = vb.right;
          gradScaleHoriz.scale = 1 / diag.scale;
          // Update properties of vertical scale to reflect viewport
          gradScaleVert.elt(0).height = vb.height * diag.scale;
          gradScaleVert.location = new go.Point(vb.x, vb.y);
          gradScaleVert.graduatedMin = vb.y;
          gradScaleVert.graduatedMax = vb.bottom;
          gradScaleVert.scale = 1 / diag.scale;
        }, null);
      }

      function updateIndicators() {
        var vb = myDiagram.viewportBounds;
        if (!vb.isReal()) return;
        myDiagram.commit(function(diag) {
          var mouseCoords = diag.lastInput.documentPoint;
          // Keep the indicators in line with the mouse as viewport changes or mouse moves
          gradIndicatorHoriz.location = new go.Point(Math.max(mouseCoords.x, vb.x), vb.y);
          gradIndicatorHoriz.scale = 1 / diag.scale;
          gradIndicatorVert.location = new go.Point(vb.x, Math.max(mouseCoords.y, vb.y));
          gradIndicatorVert.scale = 1 / diag.scale;
        }, null);
      }

      // Show indicators on mouseEnter of the diagram div; hide on mouseLeave
      function showIndicators(show) {
        myDiagram.commit(function(diag) {
          gradIndicatorHoriz.visible = show;
          gradIndicatorVert.visible = show;
        }, null);
      }

      // print the diagram by opening a new window holding SVG images of the diagram contents for each page
      function printDiagram() {
        var svgWindow = window.open();
        if (!svgWindow) return;  // failure to open a new Window
        var printSize = new go.Size(700, 960);
        var bnds = myDiagram.documentBounds.copy().unionRect(myDiagram.viewportBounds);
        var x = bnds.x;
        var y = bnds.y;
        while (y < bnds.bottom) {
          while (x < bnds.right) {
            var pagerect = new go.Rect(x, y, printSize.width, printSize.height);
            updateScales(pagerect);
            var svg = myDiagram.makeSvg({
              scale: 1.0,
              position: new go.Point(x, y),
              size: printSize,
              showGrid: true,
              showTemporary: true
            });
            svgWindow.document.body.appendChild(svg);
            x += printSize.width;
          }
          x = bnds.x;
          y += printSize.height;
        }
        // invoke the browser's print command
        setTimeout(function() { svgWindow.print(); updateScales(); }, 1);
      }

      document.getElementById("myPrintButton").addEventListener("click", printDiagram);
    }

    window.addEventListener('DOMContentLoaded', init);
  </script>

<div id="sample">
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
  <p>
    This sample demonstrates a diagram with rulers at its edges and indicators which display the mouse's position.
  </p>
  <p>
    The rulers are implemented using <a href="../intro/graduatedPanels.html">Graduated Panels</a>. The main element of each panel is sized
    according to the width/height of the viewport, with the <a>Panel.graduatedMin</a> and <a>Panel.graduatedMax</a>
    being set to the edges of the viewport.
  </p>
  <p>
    Event listeners and Tool overrides are used to keep the rulers and indicators in sync as the viewport bounds change
    or the mouse moves around the diagram.
    <ul>
      <li>
        <code>ViewportBoundsChanged</code> listeners are used to keep the rulers and indicators against
        the edge of the diagram and to update the min and max values of the rulers.
      </li>
      <li>
        An <code>InitialLayoutCompleted</code> listener is used for initial placement after the diagram
        has positioned the rest of the nodes.
      </li>
      <li>
        <a>ToolManager.doMouseMove</a>, <a>LinkingTool.doMouseMove</a>, <a>DraggingTool.doMouseMove</a>,
        and <a>DragSelectingTool.doMouseMove</a> are overridden to update the mouse indicators after executing
        their default behavior. Each is overridden so that whichever tool is active will properly adjust the
        indicators in addition to its normal functionality.
      </li>
      <li>
        Finally, the Diagram's <a>Diagram.mouseEnter</a> and <a>Diagram.mouseLeave</a> event handlers show and hide
        the indicators as the mouse moves into or out of the diagram's HTMLDivElement.
      </li>
    </ul>
  </p>
  <p>
    The rulers and the indicators are implemented using simple <a>Part</a>s, not <a>Node</a>s, so that they
    are not treated as nodes by some layouts and so that they do not show up in the collection of <a>Diagram.nodes</a>.
    They are put in the "Grid" <a>Layer</a> so that any changes to them are not recorded
    by the UndoManager, because the "Grid" Layer has all of its Parts ignored by the UndoManager.
  </p>
  <button id="myPrintButton">Print</button>
</div>
    </div>
    <!-- * * * * * * * * * * * * * -->
    <!--  End of GoJS sample code  -->
  </div>
</body>
<!--  This script is part of the gojs.net website, and is not needed to run the sample -->
<script src="../assets/js/goSamples.js"></script>
</html>
